Add IGC for flight recorders. From Chris Jones .
authorrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Mon, 30 Aug 2004 13:44:01 +0000 (13:44 +0000)
committerrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Mon, 30 Aug 2004 13:44:01 +0000 (13:44 +0000)
17 files changed:
gpsbabel/Makefile
gpsbabel/README
gpsbabel/README.igc [new file with mode: 0644]
gpsbabel/defs.h
gpsbabel/guibabel
gpsbabel/igc.c [new file with mode: 0644]
gpsbabel/main.c
gpsbabel/reference/igc1.gpx [new file with mode: 0644]
gpsbabel/reference/igc1_gpx.out [new file with mode: 0644]
gpsbabel/reference/igc1_igc.out [new file with mode: 0644]
gpsbabel/reference/igc2.igc [new file with mode: 0644]
gpsbabel/reference/igc2_gpx.out [new file with mode: 0644]
gpsbabel/reference/igc2_igc.out [new file with mode: 0644]
gpsbabel/route.c
gpsbabel/testo
gpsbabel/util.c
gpsbabel/vecs.c

index 33a665e13a82b666491d4d436d1723a005883463..e5f3f488c2a9746fe9a2ee0699eb26265b9543a8 100644 (file)
@@ -19,7 +19,8 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \
        psp.o holux.o garmin.o tmpro.o tpg.o \
        xcsv.o gcdb.o tiger.o internal_styles.o easygps.o quovadis.o \
        gpilots.o saroute.o navicache.o psitrex.o geoniche.o delgpl.o \
-       ozi.o nmea.o text.o html.o palmdoc.o netstumbler.o hsa_ndv.o
+       ozi.o nmea.o text.o html.o palmdoc.o netstumbler.o hsa_ndv.o \
+       igc.o
 
 FILTERS=position.o duplicate.o arcdist.o polygon.o smplrout.o reverse_route.o sort.o
 
index a89dbc37c369f62d3fbb1ce30edd3652eec397fa..868e40baa8e62c87c9e4c036e3b7d21172b36b94 100644 (file)
@@ -593,6 +593,12 @@ THE FORMATS
 
          snmac    - Always use the MAC address as the shortname.
 
+    IGC
+
+        FAI/IGC Data File -- Used by the international gliding community to
+        record gliding flights.  IGC files can be converted to and from tracks
+        representing recorded flights, and routes representing task
+        declarations in other formats.
 
 DATA FILTERS
 
diff --git a/gpsbabel/README.igc b/gpsbabel/README.igc
new file mode 100644 (file)
index 0000000..9603c69
--- /dev/null
@@ -0,0 +1,93 @@
+IGC Data Format Notes.
+======================
+Refer to Appendix 1 of http://www.fai.org:81/gliding/gnss/tech_spec_gnss.asp 
+for the specification of the IGC data format.
+
+A sample list of software applications that use data in IGC format can be
+found at http://www.fai.org:81/gliding/gnss/gnss_analysis_software.pdf
+
+GPSBabel can be used to translate data in IGC format to and from various other
+formats.
+
+Routes in other formats are used to represent IGC task declarations.
+Tracks in other formats are used to represent IGC recorded flights.
+
+
+
+Converting to IGC format
+========================
+IGC files generated by GPSBabel will NOT pass security validation tests since
+the data they contain cannot be proven to originate from an approved flight
+recorder.  For most software applications that use IGC files this is not an
+issue but for competition scoring, record and badge claims the generated files
+will not be accepted as proof of a flight.
+
+A track stored in another format (GPX for example) representing a recorded
+flight can be converted into an IGC file:
+
+    gpsbabel -i gpx -f mytrk.gpx -o igc -F myflight.igc
+
+If multiple track segments are provided in the input file, the one with the
+most points will be used.
+
+A route stored in another format representing a task declaration can be
+converted into an IGC file:
+
+    gpsbabel -i gpx -f myrte.gpx -o igc -F mytask.igc
+
+A route and a track in other formats can be included into a single IGC file:
+
+    gpsbabel -i gpx -f mytrk.gpx -f myrte.gpx -o igc -F myflight.igc
+
+A similar result can be obtained by downloading the track log and routes
+directly from a GPS device connected to a PC.  For example to create an IGC
+file from data recorded in a Garmin GPS connected to the first serial port of
+a PC running Linux:
+
+    gpsbabel -t -r -i garmin -f /dev/ttyS0 -o igc -F myflight.igc
+
+For Windows operating systems:
+
+    gpsbabel -t -r -i garmin -f com1 -o igc -F myflight.igc
+
+A waypoint file in another format containing a waypoint whose short name is
+"PILOT" can be merged into an IGC file.  The description field of the waypoint
+will be used for the pilot name in the IGC file header:
+
+    gpsbabel -i gpx -f mytrk.gpx -f myrte.gpx -f mywpt.gpx -o igc -F myflight.igc
+    gpsbabel -w -t -r -i garmin -f /dev/ttyS0 -o igc -F myflight.igc
+    
+Some formats such as GPX allow routes, tracks and waypoints to exist in the
+same file and can be used to fully populate an IGC file:
+
+    gpsbabel -i gpx -f myall.gpx -o igc -F myflight.igc
+
+
+
+Converting from IGC format
+==========================
+Data in an IGC file can be converted into other formats.  For example to
+generate OziExplorer files containing tracks representing the recorded
+flight (myozi.plt) and routes representing declared tasks (myozi.rte):
+
+    gpsbabel -i igc -f myflight.igc -o ozi -F myozi
+
+Or to GPX format:
+
+    gpsbabel -i igc -f myflight.igc -o gpx -F myflight.gpx
+
+Header information from the IGC file will be written to the description field
+of the track(s).
+
+If both pressure altitude and GNSS altitude are recorded in the IGC file, two
+tracks will be written to the new track file, representing the two altitude
+tracks.  The latitude, longitude and timestamps in the tracks will be identical.
+
+A route stored in another format can be merged with an existing IGC file that
+has no task declaration, to generate a new IGC file with a task declaration:
+
+    gpsbabel -i igc -f myflight.igc -i gpx -f myrte.gpx -o igc -F mynew.igc
+
+
+Chris Jones
+Aug 2004
index a6fbfe7f6553111a6341e49362ba4e66625cae75..2875ecc5a389fce64e3fcb8a05678a0857ab7fa5 100644 (file)
@@ -84,6 +84,7 @@ typedef struct {
 } global_options;
 
 extern global_options global_opts;
+extern const char gpsbabel_version[];
 
 /*
  * Extended data if waypoint happens to represent a geocache.  This is 
@@ -267,8 +268,10 @@ void route_add (waypoint *);
 void route_add_wpt(route_head *rte, waypoint *wpt);
 void route_del_wpt(route_head *rte, waypoint *wpt);
 void route_add_head(route_head *rte);
+void route_del_head(route_head *rte);
 void route_reverse(const route_head *rte_hd);
 void track_add_head(route_head *rte);
+void track_del_head(route_head *rte);
 void route_disp_all(route_hdr, route_trl, waypt_cb);
 void track_disp_all(route_hdr, route_trl, waypt_cb);
 void route_free (route_head *);
@@ -431,6 +434,8 @@ void debug_mem_close();
 #endif /* DEBUG_MEM */
 
 FILE *xfopen(const char *fname, const char *type, const char *errtxt);
+void xfprintf(const char *errtxt, FILE *stream, const char *format, ...);
+void xfputs(const char *errtxt, const char *s, FILE *stream);
 
 int case_ignore_strcmp(const char *s1, const char *s2);
 
index 3370667c3d5ffefc7c837b61aa198c7deb95e3b9..7872779a923448a06bff64686f3c737bd6d7ab5c 100755 (executable)
@@ -54,7 +54,7 @@ foreach i {read write} {
 # TODO: Get this list from 'gpsbabel -?' instead of hardcoding it here.
     tk_optionMenu $f.ftypes $ftype geo gpsman gpx \
        magellan mapsend pcx mapsource gpsutil tiger csv xmap dna psp \
-       cetus gpspilot magnav garmin mxf holux ozi tpg
+       cetus gpspilot magnav garmin mxf holux ozi tpg igc
     pack $f.lab -side left
     pack $f.ent -side left -expand yes -fill x
     pack $f.but -side left
diff --git a/gpsbabel/igc.c b/gpsbabel/igc.c
new file mode 100644 (file)
index 0000000..ef2af23
--- /dev/null
@@ -0,0 +1,903 @@
+/*
+ * FAI/IGC data format translation.
+ * 
+ * Refer to Appendix 1 of
+ * http://www.fai.org:81/gliding/gnss/tech_spec_gnss.asp for the
+ * specification of the IGC data format.  This translation code was
+ * written when the latest ammendment list for the specification was AL6.
+ * 
+ * Copyright (C) 2004 Chris Jones
+ * 
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA 02111 USA 
+ */
+
+#include "defs.h"
+#include <errno.h>
+
+static FILE *file_in;
+static FILE *file_out;
+static char manufacturer[4];
+static const route_head *head;
+static char *timeadj = NULL;
+
+#define MYNAME "IGC"
+#define MAXRECLEN 79           // Includes null terminator and CR/LF
+#define MAXDESCLEN 1024
+#define PRESTRKNAME "PRESALTTRK"
+#define GNSSTRKNAME "GNSSALTTRK"
+#define HDRMAGIC "IGCHDRS"
+#define HDRDELIM "~"
+#define DATEMAGIC "IGCDATE"
+
+/*
+ * IGC record types.
+ * These appear as the first char in each record.
+ */
+typedef enum {
+    rec_manuf_id = 'A',                // FR manufacturer and identification
+    rec_fix = 'B',             // Fix
+    rec_task = 'C',            // Task/declaration
+    rec_diff_gps = 'D',                // Differential GPS
+    rec_event = 'E',           // Event
+    rec_constel = 'F',         // Constellation
+    rec_security = 'G',                // Security
+    rec_header = 'H',          // File header
+    rec_fix_defn = 'I',                // List of extension data included at end of each fix (B) record
+    rec_extn_defn = 'J',       // List of data included in each extension (K) record
+    rec_extn_data = 'K',       // Extension data
+    rec_log_book = 'L',                // Logbook/comments
+
+    // M..Z are spare
+
+    rec_none = 0,              // No record
+    rec_bad = 1,               // Bad record
+} igc_rec_type_t;
+
+/*
+ * See if two lat/lon pairs are approximately equal.
+ * @param  lat1  The latitude of coordinate pair 1
+ * @param  lon1  The longitude of coordinate pair 1
+ * @param  lat2  The latitude of coordinate pair 2
+ * @param  lon2  The longitude of coordinate pair 2
+ * @retval  1  The coordinates are approximately equal
+ * @retval  0  The coordinates are significantly different
+ */
+static unsigned char coords_match(double lat1, double lon1, double lat2, double lon2)
+{
+    return (fabs(lat1 - lat2) < 0.0001 && fabs(lon1 - lon2) < 0.0001) ? 1 : 0;
+}
+
+/*************************************************************************************************
+ * Input file processing 
+ */
+
+/*
+ * Get an IGC record from the input file
+ * @param  rec  Caller allocated storage for the record.  At least MAXRECLEN chars must be allocated.
+ * @return the record type.  rec_none on EOF, rec_bad on fgets() or parse error.
+ */
+static igc_rec_type_t get_record(char *rec)
+{
+    size_t len;
+
+    if (fgets(rec, MAXRECLEN, file_in) == NULL) {
+       if (feof(file_in)) {
+           return rec_none;
+       } else {
+           warning(MYNAME " fgets(): %s\n", strerror(errno));
+           return rec_bad;
+       }
+    }
+    len = strlen(rec);
+    if (len < 3 || rec[len - 2] != '\r' || rec[len - 1] != '\n' || rec[0] < 'A' || rec[0] > 'Z') {
+       warning(MYNAME " bad input record: '%s'\n", rec);
+       return rec_bad;
+    }
+    rec[len - 2] = '\0';
+    return (igc_rec_type_t) rec[0];
+}
+
+static void rd_init(const char *fname)
+{
+    char ibuf[MAXRECLEN];
+
+    file_in = xfopen(fname, "rb", MYNAME);
+
+    // File must begin with a manufacturer/ID record
+    if (get_record(ibuf) != rec_manuf_id || sscanf(ibuf, "A%3[A-Z]", manufacturer) != 1) {
+       fatal(MYNAME ": %s is not an IGC file\n", fname);
+    }
+}
+
+static void rd_deinit(void)
+{
+    fclose(file_in);
+}
+
+/**
+ * Handle pre- or post-flight task declarations.
+ * A route is created for each set of waypoints in a task declaration.
+ * @param rec A single task record
+ */
+static void igc_task_rec(const char *rec)
+{
+    static char flight_date[7];
+    static unsigned int num_tp, tp_ct;
+    static route_head *rte_head;
+    static time_t creation;
+
+    char task_num[5];
+    char task_desc[MAXRECLEN];
+    waypoint *wpt;
+    unsigned int lat_deg, lat_min, lat_frac;
+    unsigned int lon_deg, lon_min, lon_frac;
+    char lat_hemi[2], lon_hemi[2];
+    char short_name[8];
+    char tmp_str[MAXRECLEN];
+    struct tm tm;
+
+    static enum { id, takeoff, start, turnpoint, finish, landing } state = id;
+
+    // First task record identifies the task to follow
+    if (id == state) {
+       task_desc[0] = '\0';
+       if (sscanf(rec, "C%2u%2u%2u%2u%2u%2u%6[0-9]%4c%2u%[^\r]\r\n",
+                  &tm.tm_mday, &tm.tm_mon, &tm.tm_year,
+                  &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+                  flight_date, task_num, &num_tp, task_desc) < 9) {
+           fatal(MYNAME ": task id (C) record parse error\n'%s'", rec);
+       }
+       task_num[4] = '\0';
+       tm.tm_mon -= 1;
+       if (tm.tm_year < 70) {
+           tm.tm_year += 100;
+       }
+       tm.tm_isdst = 0;
+       creation = mktime(&tm) + get_tz_offset();
+
+       // Create a route to store the task data in.
+       rte_head = route_head_alloc();
+       rte_head->rte_name = xstrdup(task_num);
+       sprintf(tmp_str, DATEMAGIC "%s: %s", flight_date, task_desc);
+       rte_head->rte_desc = xstrdup(tmp_str);
+       route_add_head(rte_head);
+       state++;
+       return;
+    }
+    // Get the waypoint
+    tmp_str[0] = '\0';
+    if (sscanf(rec, "C%2u%2u%3u%1[NS]%3u%2u%3u%1[WE]%[^\r]\r\n",
+              &lat_deg, &lat_min, &lat_frac, lat_hemi,
+              &lon_deg, &lon_min, &lon_frac, lon_hemi, tmp_str) < 8) {
+       fatal(MYNAME ": task waypoint (C) record parse error\n%s", rec);
+    }
+
+    wpt = waypt_new();
+    wpt->latitude = ('N' == lat_hemi[0] ? 1 : -1) *
+       (lat_deg + (lat_min * 1000 + lat_frac) / 1000.0 / 60);
+
+    wpt->longitude = ('E' == lon_hemi[0] ? 1 : -1) *
+       (lon_deg + (lon_min * 1000 + lon_frac) / 1000.0 / 60);
+
+    wpt->creation_time = creation;
+    wpt->description = xstrdup(tmp_str);
+
+    // Name the waypoint according to the order of the task record
+    switch (state) {
+    case takeoff:
+       snprintf(short_name, 8, "TAKEOFF");
+       state++;
+       break;
+
+    case start:
+       snprintf(short_name, 8, "START");
+       tp_ct = 0;
+       state++;
+       break;
+
+    case turnpoint:
+       if (++tp_ct == num_tp) {
+           state++;
+       }
+       snprintf(short_name, 8, "TURN%02u", tp_ct);
+       break;
+
+    case finish:
+       snprintf(short_name, 8, "FINISH");
+       state++;
+       break;
+
+    case landing:
+       snprintf(short_name, 8, "LANDING");
+       state = id;
+       break;
+
+    default:
+       fatal(MYNAME ": task id (C) record internal error\n%s", rec);
+       break;
+    }
+
+    // Zero lat and lon indicates an unknown waypoint
+    if (coords_match(wpt->latitude, wpt->longitude, 0.0, 0.0)) {
+       waypt_free(wpt);
+       return;
+    }
+    wpt->shortname = xstrdup(short_name);
+    route_add_wpt(rte_head, wpt);
+}
+
+static void data_read(void)
+{
+    char ibuf[MAXRECLEN];
+    igc_rec_type_t rec_type;
+    unsigned int hours, mins, secs;
+    unsigned int lat_deg, lat_min, lat_frac;
+    unsigned int lon_deg, lon_min, lon_frac;
+    char lat_hemi[2], lon_hemi[2];
+    char validity;
+    route_head *pres_head = NULL;
+    route_head *gnss_head = NULL;
+    int pres_alt, gnss_alt;
+    char pres_valid = 0;
+    char gnss_valid = 0;
+    waypoint *pres_wpt = NULL;
+    waypoint *gnss_wpt = NULL;
+    time_t date = 0;
+    time_t prev_tod = 0;
+    time_t tod;
+    struct tm tm;
+    char tmp_str[20];
+    char *hdr_data;
+    size_t remain;
+    char trk_desc[MAXDESCLEN + 1];
+
+    strcpy(trk_desc, HDRMAGIC HDRDELIM);
+
+    while (1) {
+       rec_type = get_record(ibuf);
+       switch (rec_type) {
+       case rec_manuf_id:
+           // Manufacturer/ID record already found in rd_init().
+           warning(MYNAME ": duplicate manufacturer/ID record\n");
+           break;
+
+       case rec_header:
+           // Get the header sub type
+           if (sscanf(ibuf, "H%*1[FOP]%3s", tmp_str) != 1) {
+               fatal(MYNAME ": header (H) record parse error\n%s\n%s\n", ibuf, tmp_str);
+           }
+           // Optional long name of record sub type is followed by a
+           // colon.  Actual header data follows that.
+           if (NULL == (hdr_data = strchr(ibuf, ':'))) {
+               hdr_data = ibuf + 5;
+           } else {
+               hdr_data++;
+           }
+
+           // Date sub type
+           if (strcmp(tmp_str, "DTE") == 0) {
+               if (sscanf(hdr_data, "%2u%2u%2u", &tm.tm_mday, &tm.tm_mon, &tm.tm_year) != 3) {
+                   fatal(MYNAME ": date (H) record parse error\n'%s'\n", ibuf);
+               }
+               tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+               tm.tm_mon -= 1;
+               if (tm.tm_year < 70) {
+                   tm.tm_year += 100;
+               }
+               tm.tm_isdst = 0;
+               date = mktime(&tm) + get_tz_offset();
+           } else {
+               // Store other header data in the track descriptions
+               if (strlen(trk_desc) < MAXDESCLEN) {
+                   strcat(ibuf, HDRDELIM);
+                   remain = MAXDESCLEN - strlen(trk_desc);
+                   strncat(trk_desc, ibuf, remain);
+               }
+           }
+           break;
+
+       case rec_fix:
+           // Date must appear in file before the first fix record
+           if (date < 1000000L) {
+               fatal(MYNAME ": bad date %ld\n", date);
+           }
+           // Create a track for pressure altitude waypoints
+           if (!pres_head) {
+               pres_head = route_head_alloc();
+               pres_head->rte_name = xstrdup(PRESTRKNAME);
+               pres_head->rte_desc = xstrdup(trk_desc);
+               track_add_head(pres_head);
+           }
+           // Create a second track for GNSS altitude waypoints
+           if (!gnss_head) {
+               gnss_head = route_head_alloc();
+               gnss_head->rte_name = xstrdup(GNSSTRKNAME);
+               gnss_head->rte_desc = xstrdup(trk_desc);
+               track_add_head(gnss_head);
+           }
+           // Create a waypoint from the fix record data
+           if (sscanf(ibuf,
+                      "B%2u%2u%2u%2u%2u%3u%1[NS]%3u%2u%3u%1[WE]%c%5d%5d",
+                      &hours, &mins, &secs, &lat_deg, &lat_min, &lat_frac,
+                      lat_hemi, &lon_deg, &lon_min, &lon_frac, lon_hemi,
+                      &validity, &pres_alt, &gnss_alt) != 14) {
+               fatal(MYNAME ": fix (B) record parse error\n%s\n", ibuf);
+           }
+           pres_wpt = waypt_new();
+
+           pres_wpt->latitude = ('N' == lat_hemi[0] ? 1 : -1) *
+               (lat_deg + (lat_min * 1000 + lat_frac) / 1000.0 / 60);
+
+           pres_wpt->longitude = ('E' == lon_hemi[0] ? 1 : -1) *
+               (lon_deg + (lon_min * 1000 + lon_frac) / 1000.0 / 60);
+
+           // Increment date if we pass midnight UTC
+           tod = (hours * 60 + mins) * 60 + secs;
+           if (tod < prev_tod) {
+               date += 24 * 60 * 60;
+           }
+           prev_tod = tod;
+           pres_wpt->creation_time = date + tod;
+
+           // Add the waypoint to the pressure altitude track
+           if (pres_alt) {
+               pres_valid = 1;
+               pres_wpt->altitude = pres_alt;
+           } else {
+               pres_wpt->altitude = unknown_alt;
+           }
+           route_add_wpt(pres_head, pres_wpt);
+
+           // Add the same waypoint with GNSS altitude to the second
+           // track
+           gnss_wpt = waypt_dupe(pres_wpt);
+
+           if (gnss_alt) {
+               gnss_valid = 1;
+               gnss_wpt->altitude = gnss_alt;
+           } else {
+               gnss_wpt->altitude = unknown_alt;
+           }
+           route_add_wpt(gnss_head, gnss_wpt);
+           break;
+
+       case rec_task:
+           // Create a route for each pre-flight declaration
+           igc_task_rec(ibuf);
+           break;
+
+       case rec_log_book:
+           // Get the log book sub type
+           if (sscanf(ibuf, "L%3s", tmp_str) != 1) {
+               fatal(MYNAME ": log book (L) record parse error\n'%s'\n", ibuf);
+           }
+
+           if (strcmp(tmp_str, "PFC") == 0) {
+               // Create a route for each post-flight declaration
+               igc_task_rec(ibuf + 4);
+               break;
+           } else if (global_opts.debug_level) {
+               if (strcmp(tmp_str, "OOI") == 0) {
+                   fputs(MYNAME ": Observer Input> ", stdout);
+               } else if (strcmp(tmp_str, "PLT") == 0) {
+                   fputs(MYNAME ": Pilot Input> ", stdout);
+               } else if (strcmp(tmp_str, manufacturer) == 0) {
+                   fputs(MYNAME ": Manufacturer Input> ", stdout);
+               } else {
+                   fputs(MYNAME ": Anonymous Input> ", stdout);
+                   fputs(ibuf + 1, stdout);
+                   break;
+               }
+               fputs(ibuf + 4, stdout);
+               putchar('\n');
+           }
+           break;
+
+           // These record types are discarded
+       case rec_diff_gps:
+       case rec_event:
+       case rec_constel:
+       case rec_security:
+       case rec_fix_defn:
+       case rec_extn_defn:
+       case rec_extn_data:
+           break;
+
+           // No more records
+       case rec_none:
+
+           // Include pressure altitude track only if it has useful
+           // altitude data or if it is the only track available.
+           if (pres_head && !pres_valid && gnss_head) {
+               track_del_head(pres_head);
+               pres_head = NULL;
+           }
+           // Include GNSS altitude track only if it has useful altitude
+           // data or if it is the only track available.
+           if (gnss_head && !gnss_valid && pres_head) {
+               track_del_head(gnss_head);
+           }
+           return;             // All done so bail
+
+       default:
+       case rec_bad:
+           fatal(MYNAME ": failure reading file\n");
+           break;
+       }
+    }
+}
+
+/*************************************************************************************************
+ * Output file processing 
+ */
+
+/*************************************************
+ * Callbacks used to scan for specific track types
+ */
+
+static void detect_pres_track(const route_head * rh)
+{
+    if (rh->rte_name && strncmp(rh->rte_name, PRESTRKNAME, 6) == 0) {
+       head = rh;
+    }
+}
+
+static void detect_gnss_track(const route_head * rh)
+{
+    if (rh->rte_name && strncmp(rh->rte_name, GNSSTRKNAME, 6) == 0) {
+       head = rh;
+    }
+}
+
+static void detect_other_track(const route_head * rh)
+{
+    static int max_waypt_ct;
+
+    if (!head) {
+       max_waypt_ct = 0;
+    }
+    // Find other track with the most waypoints
+    if (rh->rte_waypt_ct > max_waypt_ct &&
+       (!rh->rte_name ||
+        (strncmp(rh->rte_name, PRESTRKNAME, 6) != 0 &&
+         strncmp(rh->rte_name, GNSSTRKNAME, 6) != 0))) {
+       head = rh;
+       max_waypt_ct = rh->rte_waypt_ct;
+    }
+}
+
+/*
+ * Identify the pressure altitude and GNSS altitude tracks.
+ * @param  pres_track  Set by the function to the pressure altitude track
+ *                     head.  NULL if not found.
+ * @param  gnss_track  Set by the function to the GNSS altitude track
+ *                     head.  NULL if not found.
+ */
+static void get_tracks(const route_head ** pres_track, const route_head ** gnss_track)
+{
+    head = NULL;
+    track_disp_all(detect_pres_track, NULL, NULL);
+    *pres_track = head;
+
+    head = NULL;
+    track_disp_all(detect_gnss_track, NULL, NULL);
+    *gnss_track = head;
+
+    head = NULL;
+    track_disp_all(detect_other_track, NULL, NULL);
+
+    if (!*pres_track && *gnss_track && head) {
+       *pres_track = head;
+    }
+
+    if (!*gnss_track && head) {
+       *gnss_track = head;
+    }
+}
+
+/*************************************************
+ * IGC string formatting functions
+ */
+
+static char *latlon2str(const waypoint * wpt)
+{
+    static char str[18] = "";
+    char lat_hemi = wpt->latitude < 0 ? 'S' : 'N';
+    char lon_hemi = wpt->longitude < 0 ? 'W' : 'E';
+    unsigned char lat_deg = fabs(wpt->latitude);
+    unsigned char lon_deg = fabs(wpt->longitude);
+    unsigned int lat_min = (fabs(wpt->latitude) - lat_deg) * 60000 + 0.5;
+    unsigned int lon_min = (fabs(wpt->longitude) - lon_deg) * 60000 + 0.5;
+
+    if (snprintf(str, 18, "%02u%05u%c%03u%05u%c",
+                lat_deg, lat_min, lat_hemi, lon_deg, lon_min, lon_hemi) != 17) {
+       fatal(MYNAME ": Bad waypoint format '%s'\n", str);
+    }
+    return str;
+}
+
+static char *date2str(struct tm *dt)
+{
+    static char str[7] = "";
+
+    if (snprintf(str, 7, "%02u%02u%02u", dt->tm_mday, dt->tm_mon + 1, dt->tm_year % 100) != 6) {
+       fatal(MYNAME ": Bad date format '%s'\n", str);
+    }
+    return str;
+}
+
+static char *tod2str(struct tm *tod)
+{
+    static char str[7] = "";
+
+    if (snprintf(str, 7, "%02u%02u%02u", tod->tm_hour, tod->tm_min, tod->tm_sec) != 6) {
+       fatal(MYNAME ": Bad time of day format '%s'\n", str);
+    }
+    return str;
+}
+
+/*
+ * Write header records
+ */
+static void wr_header(void)
+{
+    const route_head *pres_track;
+    const route_head *track;
+    struct tm *tm;
+    time_t date;
+    static const char dflt_str[] = "Unknown";
+    const char *str;
+    waypoint *wpt;
+
+    get_tracks(&pres_track, &track);
+    if (!track && pres_track) {
+       track = pres_track;
+    }
+    // Date in header record is that of the first fix record
+    date = !track ? current_time() :
+       ((waypoint *) QUEUE_FIRST(&track->waypoint_list))->creation_time;
+
+    if (NULL == (tm = gmtime(&date))) {
+       fatal(MYNAME ": Bad track timestamp\n");
+    }
+    xfprintf(MYNAME, file_out, "HFDTE%s\r\n", date2str(tm));
+
+    // Other header data may have been stored in track description
+    if (track && track->rte_desc && strncmp(track->rte_desc, HDRMAGIC, strlen(HDRMAGIC)) == 0) {
+       for (str = strtok(track->rte_desc + strlen(HDRMAGIC) + strlen(HDRDELIM), HDRDELIM);
+            str; str = strtok(NULL, HDRDELIM)) {
+           xfprintf(MYNAME, file_out, "%s\r\n", str);
+       }
+    } else {
+       // IGC header info not found so synthesise it.
+       // If a waypoint is supplied with a short name of "PILOT", use
+       // its description as the pilot's name in the header.
+       str = dflt_str;
+       if (NULL != (wpt = find_waypt_by_name("PILOT")) && wpt->description) {
+           str = wpt->description;
+       }
+       xfprintf(MYNAME, file_out, "HFPLTPILOT:%s\r\n", str);
+    }
+}
+
+/*************************************************
+ * Generation of IGC task declaration records
+ */
+
+static void wr_task_wpt_name(const waypoint * wpt, const char *alt_name)
+{
+    xfprintf(MYNAME, file_out, "C%s%s\r\n", latlon2str(wpt),
+            wpt->description ? wpt->description : wpt->shortname ? wpt->shortname : alt_name);
+}
+
+static void wr_task_hdr(const route_head * rte)
+{
+    unsigned char have_takeoff = 0;
+    const waypoint *wpt;
+    char flight_date[7] = "000000";
+    char task_desc[MAXRECLEN] = "";
+    int num_tps = rte->rte_waypt_ct - 2;
+    struct tm *tm;
+    time_t rte_time;
+    static unsigned int task_num = 1;
+
+    if (num_tps < 0) {
+       fatal(MYNAME ": Empty task route\n");
+    }
+    // See if the takeoff and landing waypoints are there or if we need to
+    // generate them.
+    wpt = (waypoint *) QUEUE_LAST(&rte->waypoint_list);
+    if (wpt->shortname && strncmp(wpt->shortname, "LANDING", 6) == 0) {
+       num_tps--;
+    }
+    wpt = (waypoint *) QUEUE_FIRST(&rte->waypoint_list);
+    if (wpt->shortname && strncmp(wpt->shortname, "TAKEOFF", 6) == 0) {
+       have_takeoff = 1;
+       num_tps--;
+    }
+    if (num_tps < 0) {
+       fatal(MYNAME ": Too few waypoints in task route\n");
+    }
+    // Gather data to write to the task identification (first) record
+    rte_time = wpt->creation_time ? wpt->creation_time : current_time();
+    if (NULL == (tm = gmtime(&rte_time))) {
+       fatal(MYNAME ": Bad task route timestamp\n");
+    }
+
+    if (rte->rte_desc) {
+       sscanf(rte->rte_desc, DATEMAGIC "%6[0-9]: %s", flight_date, task_desc);
+    }
+
+    xfprintf(MYNAME, file_out, "C%s%s%s%04u%02u%s\r\n", date2str(tm),
+            tod2str(tm), flight_date, task_num++, num_tps, task_desc);
+
+    if (!have_takeoff) {
+       // Generate the takeoff waypoint
+       wr_task_wpt_name(wpt, "TAKEOFF");
+    }
+}
+
+static void wr_task_wpt(const waypoint * wpt)
+{
+    wr_task_wpt_name(wpt, "");
+}
+
+static void wr_task_tlr(const route_head * rte)
+{
+    // If the landing waypoint is not supplied we need to generate it.
+    const waypoint *wpt = (waypoint *) QUEUE_LAST(&rte->waypoint_list);
+    if (!wpt->shortname || strncmp(wpt->shortname, "LANDIN", 6) != 0) {
+       wr_task_wpt_name(wpt, "LANDING");
+    }
+}
+
+static void wr_tasks(void)
+{
+    route_disp_all(wr_task_hdr, wr_task_tlr, wr_task_wpt);
+}
+
+/*
+ * Write a single fix record
+ */
+static void wr_fix_record(const waypoint * wpt, int pres_alt, int gnss_alt)
+{
+    struct tm *tm;
+
+    if (NULL == (tm = gmtime(&wpt->creation_time))) {
+       fatal(MYNAME ": bad track timestamp\n");
+    }
+
+    if (unknown_alt == pres_alt) {
+       pres_alt = 0;
+    }
+    if (unknown_alt == gnss_alt) {
+       gnss_alt = 0;
+    }
+    xfprintf(MYNAME, file_out, "B%02u%02u%02u%sA%05d%05d\r\n", tm->tm_hour,
+            tm->tm_min, tm->tm_sec, latlon2str(wpt), pres_alt, gnss_alt);
+}
+
+/**
+ * Attempt to align the pressure and GNSS tracks in time.
+ * This is useful when trying to merge a track (lat/lon/time) recorded by a
+ * GPS with a barograph (alt/time) recorded by a seperate instrument with
+ * independent clocks which are not closely synchronised.
+ * @return The number of seconds to add to the GNSS track in order to align
+ *         it with the pressure track.
+ */
+static int correlate_tracks(const route_head * pres_track, const route_head * gnss_track)
+{
+    const queue *elem;
+    double last_alt, alt_diff;
+    double speed;
+    time_t pres_time, gnss_time;
+    int time_diff;
+    const waypoint *wpt;
+
+    // Deduce the landing time from the pressure altitude track based on
+    // when we last descended to within 10m of the final track altitude.
+    elem = QUEUE_LAST(&pres_track->waypoint_list);
+    last_alt = ((waypoint *) elem)->altitude;
+    do {
+       elem = elem->prev;
+       if (&pres_track->waypoint_list == elem) {
+           // No track left
+           return 0;
+       }
+       alt_diff = last_alt - ((waypoint *) elem)->altitude;
+       if (alt_diff > 10.0) {
+           // Last part of track was ascending
+           return 0;
+       }
+    } while (alt_diff > -10.0);
+    pres_time = ((waypoint *) elem->next)->creation_time;
+    if (global_opts.debug_level >= 1) {
+       printf(MYNAME ": pressure landing time %s", ctime(&pres_time));
+    }
+    // Deduce the landing time from the GNSS altitude track based on
+    // when the groundspeed last dropped below a certain level.
+    elem = QUEUE_LAST(&gnss_track->waypoint_list);
+    last_alt = ((waypoint *) elem)->altitude;
+    do {
+       wpt = (waypoint *) elem;
+       elem = elem->prev;
+       if (&gnss_track->waypoint_list == elem) {
+           // No track left
+           return 0;
+       }
+       // Get a crude indication of groundspeed from the change in lat/lon
+       time_diff = wpt->creation_time - ((waypoint *) elem)->creation_time;
+       speed = !time_diff ? 0 :
+           (fabs(wpt->latitude - ((waypoint *) elem)->latitude) +
+            fabs(wpt->longitude - ((waypoint *) elem)->longitude)) / time_diff;
+       if (global_opts.debug_level >= 2) {
+           printf(MYNAME ": speed=%f\n", speed);
+       }
+    } while (speed < 0.00003);
+    gnss_time = ((waypoint *) elem->next)->creation_time;
+    if (global_opts.debug_level >= 1) {
+       printf(MYNAME ": gnss landing time %s", ctime(&gnss_time));
+    }
+    // Time adjustment is difference between the two estimated landing times
+    if (15 * 60 < abs(time_diff = pres_time - gnss_time)) {
+       warning(MYNAME ": excessive time adjustment %ds\n", time_diff);
+    }
+    return time_diff;
+}
+
+/**
+ * Interpolate altitude from a track at a given time.
+ * @param  track  The track containing altitude data.
+ * @param  time   The time that we are interested in.
+ * @return  The altitude interpolated from the track.
+ */
+static double interpolate_alt(const route_head * track, time_t time)
+{
+    static const queue *prev_elem = NULL;
+    static const queue *curr_elem = NULL;
+    const waypoint *prev_wpt;
+    const waypoint *curr_wpt;
+    int time_diff;
+    double alt_diff;
+
+    // Start search at the beginning of the track
+    if (!prev_elem) {
+       curr_elem = prev_elem = QUEUE_FIRST(&track->waypoint_list);
+    }
+    // Find the track points either side of the requested time
+    while (((waypoint *) curr_elem)->creation_time < time) {
+       if (QUEUE_LAST(&track->waypoint_list) == curr_elem) {
+           // Requested time later than all track points, we can't interpolate
+           return unknown_alt;
+       }
+       prev_elem = curr_elem;
+       curr_elem = QUEUE_NEXT(prev_elem);
+    }
+
+    prev_wpt = (waypoint *) prev_elem;
+    curr_wpt = (waypoint *) curr_elem;
+
+    if (QUEUE_FIRST(&track->waypoint_list) == curr_elem) {
+       if (curr_wpt->creation_time == time) {
+           // First point's creation time is an exact match so use it's altitude
+           return curr_wpt->altitude;
+       } else {
+           // Requested time is prior to any track points, we can't interpolate
+           return unknown_alt;
+       }
+    }
+    // Interpolate
+    if (0 == (time_diff = curr_wpt->creation_time - prev_wpt->creation_time)) {
+       // Avoid divide by zero
+       return curr_wpt->altitude;
+    }
+    alt_diff = curr_wpt->altitude - prev_wpt->altitude;
+    return prev_wpt->altitude + (alt_diff / time_diff) * (time - prev_wpt->creation_time);
+}
+
+/*
+ * Pressure altitude and GNSS altitude may be provided in two seperate
+ * tracks.  This function attempts to merge them into one.
+ */
+static void wr_track(void)
+{
+    const route_head *pres_track;
+    const route_head *gnss_track;
+    const waypoint *wpt;
+    const queue *elem;
+    const queue *tmp;
+    int time_adj;
+    double pres_alt;
+
+    // Find pressure altitude and GNSS altitude tracks
+    get_tracks(&pres_track, &gnss_track);
+
+    // If both found, attempt to merge them
+    if (pres_track && gnss_track) {
+       if (timeadj) {
+           if (strcmp(timeadj, "auto") == 0) {
+               time_adj = correlate_tracks(pres_track, gnss_track);
+           } else if (sscanf(timeadj, "%d", &time_adj) != 1) {
+               fatal(MYNAME ": bad timeadj argument '%s'\n", timeadj);
+           }
+       } else {
+           time_adj = 0;
+       }
+       if (global_opts.debug_level >= 1) {
+           printf(MYNAME ": adjusting time by %ds\n", time_adj);
+       }
+       // Iterate through waypoints in both tracks simultaneously
+       QUEUE_FOR_EACH(&gnss_track->waypoint_list, elem, tmp) {
+           wpt = (waypoint *) elem;
+           pres_alt = interpolate_alt(pres_track, wpt->creation_time + time_adj);
+           wr_fix_record(wpt, pres_alt, wpt->altitude);
+       }
+    } else {
+       if (pres_track) {
+           // Only the pressure altitude track was found so generate fix
+           // records from it alone.
+           QUEUE_FOR_EACH(&pres_track->waypoint_list, elem, tmp) {
+               wr_fix_record((waypoint *) elem, ((waypoint *) elem)->altitude, unknown_alt);
+           }
+       } else if (gnss_track) {
+           // Only the GNSS altitude track was found so generate fix
+           // records from it alone.
+           QUEUE_FOR_EACH(&gnss_track->waypoint_list, elem, tmp) {
+               wr_fix_record((waypoint *) elem, unknown_alt, ((waypoint *) elem)->altitude);
+           }
+       } else {
+           // No tracks found so nothing to do
+           return;
+       }
+    }
+}
+
+static void wr_init(const char *fname)
+{
+    file_out = xfopen(fname, "wb", MYNAME);
+}
+
+static void wr_deinit(void)
+{
+    fclose(file_out);
+}
+
+static void data_write(void)
+{
+    xfputs(MYNAME, "AXXXZZZGPSBabel\r\n", file_out);
+    wr_header();
+    wr_tasks();
+    wr_track();
+    xfprintf(MYNAME, file_out, "LXXXGenerated by GPSBabel Version %s\r\n", gpsbabel_version);
+    xfputs(MYNAME, "GGPSBabelSecurityRecordGuaranteedToFailVALIChecks\r\n", file_out);
+}
+
+
+static arglist_t igc_args[] = {
+    {"timeadj", &timeadj,
+     "(integer sec or 'auto') Barograph to GPS time diff", ARGTYPE_STRING},
+    {0, 0, 0, 0}
+};
+
+ff_vecs_t igc_vecs = {
+    ff_type_file,
+    rd_init,
+    wr_init,
+    rd_deinit,
+    wr_deinit,
+    data_read,
+    data_write,
+    igc_args
+};
index a93026afcb19fc605bdb7641f7ab73c958a31d62..576bd673cac8c2e43366de902e72f0f4068212b8 100644 (file)
@@ -22,6 +22,7 @@
 #include <ctype.h>
 
 global_options global_opts;
+const char gpsbabel_version[] = VERSION;
 
 static void
 usage(const char *pname, int shorter)
diff --git a/gpsbabel/reference/igc1.gpx b/gpsbabel/reference/igc1.gpx
new file mode 100644 (file)
index 0000000..35191c1
--- /dev/null
@@ -0,0 +1,549 @@
+<?xml version="1.0"?>
+<gpx
+ version="1.0"
+creator="GPSBabel - http://gpsbabel.sourceforge.net"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns="http://www.topografix.com/GPX/1/0"
+xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
+<time>2004-08-25T09:57:00Z</time>
+<bounds minlat="-35.498071" minlon ="148.115814" maxlat="-27.755478" maxlon="153.042490" />
+<wpt lat="-29.480116" lon="150.133619">
+  <name>PALLMG</name>
+  <cmt>PALLMG</cmt>
+  <desc>PALLMG</desc>
+  <sym>dot</sym>
+</wpt>
+<wpt lat="-27.755478" lon="149.746093">
+  <name>PILOT</name>
+  <cmt>CHRIS JONES</cmt>
+  <desc>CHRIS JONES</desc>
+  <sym>dot</sym>
+</wpt>
+<wpt lat="-30.342870" lon="150.477059">
+  <name>PLUMTH</name>
+  <cmt>PLUMTHORPE LOC</cmt>
+  <desc>PLUMTHORPE LOC</desc>
+  <sym>dot</sym>
+</wpt>
+<trk>
+<trkseg>
+<trkpt lat="-30.747492" lon="150.720524">
+<ele>0.000000</ele>
+<time>2004-04-24T00:25:32Z</time>
+</trkpt>
+<trkpt lat="-30.747492" lon="150.720529">
+<ele>0.000000</ele>
+<time>2004-04-24T00:25:33Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<trk>
+  <name> #2</name>
+<number>1</number>
+<trkseg>
+<trkpt lat="-30.687127" lon="150.611240">
+<ele>0.000000</ele>
+<time>2004-04-24T02:25:39Z</time>
+</trkpt>
+<trkpt lat="-30.687631" lon="150.609925">
+<ele>0.000000</ele>
+<time>2004-04-24T02:26:10Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<trk>
+  <name> #3</name>
+<number>2</number>
+<trkseg>
+<trkpt lat="-30.676312" lon="150.606406">
+<ele>0.000000</ele>
+<time>2004-04-24T04:00:19Z</time>
+</trkpt>
+<trkpt lat="-30.676907" lon="150.605285">
+<ele>0.000000</ele>
+<time>2004-04-24T04:00:49Z</time>
+</trkpt>
+<trkpt lat="-30.678313" lon="150.605435">
+<ele>0.000000</ele>
+<time>2004-04-24T04:01:19Z</time>
+</trkpt>
+<trkpt lat="-30.677154" lon="150.607184">
+<ele>0.000000</ele>
+<time>2004-04-24T04:01:50Z</time>
+</trkpt>
+<trkpt lat="-30.677127" lon="150.609410">
+<ele>0.000000</ele>
+<time>2004-04-24T04:02:20Z</time>
+</trkpt>
+<trkpt lat="-30.678157" lon="150.607184">
+<ele>0.000000</ele>
+<time>2004-04-24T04:02:51Z</time>
+</trkpt>
+<trkpt lat="-30.675910" lon="150.604497">
+<ele>0.000000</ele>
+<time>2004-04-24T04:03:22Z</time>
+</trkpt>
+<trkpt lat="-30.678672" lon="150.605934">
+<ele>0.000000</ele>
+<time>2004-04-24T04:03:52Z</time>
+</trkpt>
+<trkpt lat="-30.681403" lon="150.607538">
+<ele>0.000000</ele>
+<time>2004-04-24T04:04:23Z</time>
+</trkpt>
+<trkpt lat="-30.677637" lon="150.606975">
+<ele>0.000000</ele>
+<time>2004-04-24T04:04:54Z</time>
+</trkpt>
+<trkpt lat="-30.680180" lon="150.608450">
+<ele>0.000000</ele>
+<time>2004-04-24T04:05:24Z</time>
+</trkpt>
+<trkpt lat="-30.678141" lon="150.606304">
+<ele>0.000000</ele>
+<time>2004-04-24T04:05:55Z</time>
+</trkpt>
+<trkpt lat="-30.678560" lon="150.606814">
+<ele>0.000000</ele>
+<time>2004-04-24T04:06:26Z</time>
+</trkpt>
+<trkpt lat="-30.678098" lon="150.606519">
+<ele>0.000000</ele>
+<time>2004-04-24T04:06:57Z</time>
+</trkpt>
+<trkpt lat="-30.678806" lon="150.606986">
+<ele>0.000000</ele>
+<time>2004-04-24T04:07:28Z</time>
+</trkpt>
+<trkpt lat="-30.677594" lon="150.606095">
+<ele>0.000000</ele>
+<time>2004-04-24T04:07:58Z</time>
+</trkpt>
+<trkpt lat="-30.679751" lon="150.607452">
+<ele>0.000000</ele>
+<time>2004-04-24T04:08:29Z</time>
+</trkpt>
+<trkpt lat="-30.680367" lon="150.607211">
+<ele>0.000000</ele>
+<time>2004-04-24T04:09:00Z</time>
+</trkpt>
+<trkpt lat="-30.679911" lon="150.608922">
+<ele>0.000000</ele>
+<time>2004-04-24T04:09:31Z</time>
+</trkpt>
+<trkpt lat="-30.680147" lon="150.609545">
+<ele>0.000000</ele>
+<time>2004-04-24T04:10:01Z</time>
+</trkpt>
+<trkpt lat="-30.680319" lon="150.607404">
+<ele>0.000000</ele>
+<time>2004-04-24T04:10:32Z</time>
+</trkpt>
+<trkpt lat="-30.679745" lon="150.605071">
+<ele>0.000000</ele>
+<time>2004-04-24T04:11:02Z</time>
+</trkpt>
+<trkpt lat="-30.680266" lon="150.605451">
+<ele>0.000000</ele>
+<time>2004-04-24T04:11:32Z</time>
+</trkpt>
+<trkpt lat="-30.680764" lon="150.606621">
+<ele>0.000000</ele>
+<time>2004-04-24T04:12:03Z</time>
+</trkpt>
+<trkpt lat="-30.680212" lon="150.608380">
+<ele>0.000000</ele>
+<time>2004-04-24T04:12:34Z</time>
+</trkpt>
+<trkpt lat="-30.679332" lon="150.608466">
+<ele>0.000000</ele>
+<time>2004-04-24T04:13:04Z</time>
+</trkpt>
+<trkpt lat="-30.678238" lon="150.605317">
+<ele>0.000000</ele>
+<time>2004-04-24T04:13:35Z</time>
+</trkpt>
+<trkpt lat="-30.675641" lon="150.602539">
+<ele>0.000000</ele>
+<time>2004-04-24T04:14:06Z</time>
+</trkpt>
+<trkpt lat="-30.678248" lon="150.605130">
+<ele>0.000000</ele>
+<time>2004-04-24T04:14:37Z</time>
+</trkpt>
+<trkpt lat="-30.679300" lon="150.607023">
+<ele>0.000000</ele>
+<time>2004-04-24T04:15:07Z</time>
+</trkpt>
+<trkpt lat="-30.679434" lon="150.608160">
+<ele>0.000000</ele>
+<time>2004-04-24T04:15:38Z</time>
+</trkpt>
+<trkpt lat="-30.679214" lon="150.608214">
+<ele>0.000000</ele>
+<time>2004-04-24T04:16:08Z</time>
+</trkpt>
+<trkpt lat="-30.678978" lon="150.608241">
+<ele>0.000000</ele>
+<time>2004-04-24T04:16:39Z</time>
+</trkpt>
+<trkpt lat="-30.679160" lon="150.608788">
+<ele>0.000000</ele>
+<time>2004-04-24T04:17:10Z</time>
+</trkpt>
+<trkpt lat="-30.680314" lon="150.606557">
+<ele>0.000000</ele>
+<time>2004-04-24T04:17:41Z</time>
+</trkpt>
+<trkpt lat="-30.680094" lon="150.605859">
+<ele>0.000000</ele>
+<time>2004-04-24T04:18:11Z</time>
+</trkpt>
+<trkpt lat="-30.676156" lon="150.605741">
+<ele>0.000000</ele>
+<time>2004-04-24T04:18:41Z</time>
+</trkpt>
+<trkpt lat="-30.675427" lon="150.604212">
+<ele>0.000000</ele>
+<time>2004-04-24T04:19:12Z</time>
+</trkpt>
+<trkpt lat="-30.677428" lon="150.605564">
+<ele>0.000000</ele>
+<time>2004-04-24T04:19:43Z</time>
+</trkpt>
+<trkpt lat="-30.679901" lon="150.606111">
+<ele>0.000000</ele>
+<time>2004-04-24T04:20:13Z</time>
+</trkpt>
+<trkpt lat="-30.678442" lon="150.606111">
+<ele>0.000000</ele>
+<time>2004-04-24T04:20:44Z</time>
+</trkpt>
+<trkpt lat="-30.677975" lon="150.607045">
+<ele>0.000000</ele>
+<time>2004-04-24T04:21:14Z</time>
+</trkpt>
+<trkpt lat="-30.677626" lon="150.606905">
+<ele>0.000000</ele>
+<time>2004-04-24T04:21:44Z</time>
+</trkpt>
+<trkpt lat="-30.679531" lon="150.605961">
+<ele>0.000000</ele>
+<time>2004-04-24T04:22:15Z</time>
+</trkpt>
+<trkpt lat="-30.678694" lon="150.607050">
+<ele>0.000000</ele>
+<time>2004-04-24T04:22:45Z</time>
+</trkpt>
+<trkpt lat="-30.678624" lon="150.607619">
+<ele>0.000000</ele>
+<time>2004-04-24T04:23:15Z</time>
+</trkpt>
+<trkpt lat="-30.678329" lon="150.607399">
+<ele>0.000000</ele>
+<time>2004-04-24T04:23:45Z</time>
+</trkpt>
+<trkpt lat="-30.678147" lon="150.607624">
+<ele>0.000000</ele>
+<time>2004-04-24T04:24:16Z</time>
+</trkpt>
+<trkpt lat="-30.679225" lon="150.608101">
+<ele>0.000000</ele>
+<time>2004-04-24T04:24:47Z</time>
+</trkpt>
+<trkpt lat="-30.679171" lon="150.605484">
+<ele>0.000000</ele>
+<time>2004-04-24T04:25:18Z</time>
+</trkpt>
+<trkpt lat="-30.679906" lon="150.604003">
+<ele>0.000000</ele>
+<time>2004-04-24T04:25:48Z</time>
+</trkpt>
+<trkpt lat="-30.678136" lon="150.605881">
+<ele>0.000000</ele>
+<time>2004-04-24T04:26:19Z</time>
+</trkpt>
+<trkpt lat="-30.677980" lon="150.606675">
+<ele>0.000000</ele>
+<time>2004-04-24T04:26:49Z</time>
+</trkpt>
+<trkpt lat="-30.679236" lon="150.605527">
+<ele>0.000000</ele>
+<time>2004-04-24T04:27:20Z</time>
+</trkpt>
+<trkpt lat="-30.678216" lon="150.605274">
+<ele>0.000000</ele>
+<time>2004-04-24T04:27:51Z</time>
+</trkpt>
+<trkpt lat="-30.681961" lon="150.604808">
+<ele>0.000000</ele>
+<time>2004-04-24T04:28:21Z</time>
+</trkpt>
+<trkpt lat="-30.686220" lon="150.605269">
+<ele>0.000000</ele>
+<time>2004-04-24T04:28:51Z</time>
+</trkpt>
+<trkpt lat="-30.691118" lon="150.606760">
+<ele>0.000000</ele>
+<time>2004-04-24T04:29:22Z</time>
+</trkpt>
+<trkpt lat="-30.695871" lon="150.609110">
+<ele>0.000000</ele>
+<time>2004-04-24T04:29:52Z</time>
+</trkpt>
+<trkpt lat="-30.700361" lon="150.611352">
+<ele>0.000000</ele>
+<time>2004-04-24T04:30:23Z</time>
+</trkpt>
+<trkpt lat="-30.704797" lon="150.615107">
+<ele>0.000000</ele>
+<time>2004-04-24T04:30:54Z</time>
+</trkpt>
+<trkpt lat="-30.705988" lon="150.617468">
+<ele>0.000000</ele>
+<time>2004-04-24T04:31:24Z</time>
+</trkpt>
+<trkpt lat="-30.707173" lon="150.619184">
+<ele>0.000000</ele>
+<time>2004-04-24T04:31:55Z</time>
+</trkpt>
+<trkpt lat="-30.705800" lon="150.620472">
+<ele>0.000000</ele>
+<time>2004-04-24T04:32:26Z</time>
+</trkpt>
+<trkpt lat="-30.706830" lon="150.620939">
+<ele>0.000000</ele>
+<time>2004-04-24T04:32:57Z</time>
+</trkpt>
+<trkpt lat="-30.710199" lon="150.620788">
+<ele>0.000000</ele>
+<time>2004-04-24T04:33:27Z</time>
+</trkpt>
+<trkpt lat="-30.714281" lon="150.622430">
+<ele>0.000000</ele>
+<time>2004-04-24T04:33:58Z</time>
+</trkpt>
+<trkpt lat="-30.717736" lon="150.627247">
+<ele>0.000000</ele>
+<time>2004-04-24T04:34:29Z</time>
+</trkpt>
+<trkpt lat="-30.719463" lon="150.630798">
+<ele>0.000000</ele>
+<time>2004-04-24T04:35:00Z</time>
+</trkpt>
+<trkpt lat="-30.719882" lon="150.633084">
+<ele>0.000000</ele>
+<time>2004-04-24T04:35:30Z</time>
+</trkpt>
+<trkpt lat="-30.719705" lon="150.630412">
+<ele>0.000000</ele>
+<time>2004-04-24T04:36:01Z</time>
+</trkpt>
+<trkpt lat="-30.719737" lon="150.631909">
+<ele>0.000000</ele>
+<time>2004-04-24T04:36:31Z</time>
+</trkpt>
+<trkpt lat="-30.718696" lon="150.633759">
+<ele>0.000000</ele>
+<time>2004-04-24T04:37:02Z</time>
+</trkpt>
+<trkpt lat="-30.718938" lon="150.633164">
+<ele>0.000000</ele>
+<time>2004-04-24T04:37:33Z</time>
+</trkpt>
+<trkpt lat="-30.718857" lon="150.634387">
+<ele>0.000000</ele>
+<time>2004-04-24T04:38:03Z</time>
+</trkpt>
+<trkpt lat="-30.720155" lon="150.633234">
+<ele>0.000000</ele>
+<time>2004-04-24T04:38:33Z</time>
+</trkpt>
+<trkpt lat="-30.724146" lon="150.635245">
+<ele>0.000000</ele>
+<time>2004-04-24T04:39:04Z</time>
+</trkpt>
+<trkpt lat="-30.727816" lon="150.639730">
+<ele>0.000000</ele>
+<time>2004-04-24T04:39:35Z</time>
+</trkpt>
+<trkpt lat="-30.729768" lon="150.645792">
+<ele>0.000000</ele>
+<time>2004-04-24T04:40:05Z</time>
+</trkpt>
+<trkpt lat="-30.729050" lon="150.647487">
+<ele>0.000000</ele>
+<time>2004-04-24T04:40:35Z</time>
+</trkpt>
+<trkpt lat="-30.729548" lon="150.647407">
+<ele>0.000000</ele>
+<time>2004-04-24T04:41:05Z</time>
+</trkpt>
+<trkpt lat="-30.730063" lon="150.648855">
+<ele>0.000000</ele>
+<time>2004-04-24T04:41:35Z</time>
+</trkpt>
+<trkpt lat="-30.729629" lon="150.650856">
+<ele>0.000000</ele>
+<time>2004-04-24T04:42:05Z</time>
+</trkpt>
+<trkpt lat="-30.730257" lon="150.647643">
+<ele>0.000000</ele>
+<time>2004-04-24T04:42:36Z</time>
+</trkpt>
+<trkpt lat="-30.730739" lon="150.648855">
+<ele>0.000000</ele>
+<time>2004-04-24T04:43:07Z</time>
+</trkpt>
+<trkpt lat="-30.731431" lon="150.648785">
+<ele>0.000000</ele>
+<time>2004-04-24T04:43:38Z</time>
+</trkpt>
+<trkpt lat="-30.730750" lon="150.649112">
+<ele>0.000000</ele>
+<time>2004-04-24T04:44:09Z</time>
+</trkpt>
+<trkpt lat="-30.731732" lon="150.647696">
+<ele>0.000000</ele>
+<time>2004-04-24T04:44:40Z</time>
+</trkpt>
+<trkpt lat="-30.732236" lon="150.645926">
+<ele>0.000000</ele>
+<time>2004-04-24T04:45:11Z</time>
+</trkpt>
+<trkpt lat="-30.734875" lon="150.646226">
+<ele>0.000000</ele>
+<time>2004-04-24T04:45:42Z</time>
+</trkpt>
+<trkpt lat="-30.735836" lon="150.652401">
+<ele>0.000000</ele>
+<time>2004-04-24T04:46:13Z</time>
+</trkpt>
+<trkpt lat="-30.739505" lon="150.654139">
+<ele>0.000000</ele>
+<time>2004-04-24T04:46:44Z</time>
+</trkpt>
+<trkpt lat="-30.743238" lon="150.656923">
+<ele>0.000000</ele>
+<time>2004-04-24T04:47:15Z</time>
+</trkpt>
+<trkpt lat="-30.746725" lon="150.661901">
+<ele>0.000000</ele>
+<time>2004-04-24T04:47:46Z</time>
+</trkpt>
+<trkpt lat="-30.749381" lon="150.667073">
+<ele>0.000000</ele>
+<time>2004-04-24T04:48:16Z</time>
+</trkpt>
+<trkpt lat="-30.749708" lon="150.673853">
+<ele>0.000000</ele>
+<time>2004-04-24T04:48:47Z</time>
+</trkpt>
+<trkpt lat="-30.749847" lon="150.676219">
+<ele>0.000000</ele>
+<time>2004-04-24T04:49:18Z</time>
+</trkpt>
+<trkpt lat="-30.752562" lon="150.680140">
+<ele>0.000000</ele>
+<time>2004-04-24T04:49:48Z</time>
+</trkpt>
+<trkpt lat="-30.755705" lon="150.684925">
+<ele>0.000000</ele>
+<time>2004-04-24T04:50:19Z</time>
+</trkpt>
+<trkpt lat="-30.758066" lon="150.689721">
+<ele>0.000000</ele>
+<time>2004-04-24T04:50:49Z</time>
+</trkpt>
+<trkpt lat="-30.759916" lon="150.696180">
+<ele>0.000000</ele>
+<time>2004-04-24T04:51:19Z</time>
+</trkpt>
+<trkpt lat="-30.762395" lon="150.703411">
+<ele>0.000000</ele>
+<time>2004-04-24T04:51:50Z</time>
+</trkpt>
+<trkpt lat="-30.764074" lon="150.710707">
+<ele>0.000000</ele>
+<time>2004-04-24T04:52:21Z</time>
+</trkpt>
+<trkpt lat="-30.765592" lon="150.715862">
+<ele>0.000000</ele>
+<time>2004-04-24T04:52:51Z</time>
+</trkpt>
+<trkpt lat="-30.763983" lon="150.721924">
+<ele>0.000000</ele>
+<time>2004-04-24T04:53:21Z</time>
+</trkpt>
+<trkpt lat="-30.765254" lon="150.725979">
+<ele>0.000000</ele>
+<time>2004-04-24T04:53:52Z</time>
+</trkpt>
+<trkpt lat="-30.765292" lon="150.723812">
+<ele>0.000000</ele>
+<time>2004-04-24T04:54:23Z</time>
+</trkpt>
+<trkpt lat="-30.765544" lon="150.725657">
+<ele>0.000000</ele>
+<time>2004-04-24T04:54:53Z</time>
+</trkpt>
+<trkpt lat="-30.763623" lon="150.724289">
+<ele>0.000000</ele>
+<time>2004-04-24T04:55:24Z</time>
+</trkpt>
+<trkpt lat="-30.763720" lon="150.723968">
+<ele>0.000000</ele>
+<time>2004-04-24T04:55:54Z</time>
+</trkpt>
+<trkpt lat="-30.763843" lon="150.723705">
+<ele>0.000000</ele>
+<time>2004-04-24T04:56:25Z</time>
+</trkpt>
+<trkpt lat="-30.764090" lon="150.723565">
+<ele>0.000000</ele>
+<time>2004-04-24T04:56:56Z</time>
+</trkpt>
+<trkpt lat="-30.764138" lon="150.723399">
+<ele>0.000000</ele>
+<time>2004-04-24T04:57:19Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<trk>
+  <name> #4</name>
+<number>3</number>
+<trkseg>
+<trkpt lat="-30.677283" lon="150.598301">
+<ele>0.000000</ele>
+<time>2004-04-25T03:48:36Z</time>
+</trkpt>
+<trkpt lat="-30.675572" lon="150.605253">
+<ele>0.000000</ele>
+<time>2004-04-25T03:49:08Z</time>
+</trkpt>
+<trkpt lat="-30.675142" lon="150.605425">
+<ele>0.000000</ele>
+<time>2004-04-25T03:49:39Z</time>
+</trkpt>
+<trkpt lat="-30.674467" lon="150.605902">
+<ele>0.000000</ele>
+<time>2004-04-25T03:50:09Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<rte>
+  <name>AB1</name>
+  <rtept lat="-30.675867" lon="150.609035">
+    <ele>0.000000</ele>
+    <name>BORAH </name>
+  </rtept>
+  <rtept lat="-30.729929" lon="150.649193">
+    <ele>0.000000</ele>
+    <name>BALDWI</name>
+  </rtept>
+  <rtept lat="-30.763677" lon="150.723195">
+    <ele>0.000000</ele>
+    <name>MANAIR</name>
+  </rtept>
+</rte>
+</gpx>
diff --git a/gpsbabel/reference/igc1_gpx.out b/gpsbabel/reference/igc1_gpx.out
new file mode 100644 (file)
index 0000000..bfa5123
--- /dev/null
@@ -0,0 +1,378 @@
+<?xml version="1.0"?>
+<gpx
+ version="1.0"
+creator="GPSBabel - http://gpsbabel.sourceforge.net"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns="http://www.topografix.com/GPX/1/0"
+xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
+<time>1970-01-01T00:00:00Z</time>
+<trk>
+  <name>GNSSALTTRK</name>
+  <desc>IGCHDRS~HFPLTPILOT:CHRIS JONES~</desc>
+<trkseg>
+<trkpt lat="-30.676317" lon="150.606400">
+<time>2004-04-24T04:00:19Z</time>
+</trkpt>
+<trkpt lat="-30.676900" lon="150.605283">
+<time>2004-04-24T04:00:49Z</time>
+</trkpt>
+<trkpt lat="-30.678317" lon="150.605433">
+<time>2004-04-24T04:01:19Z</time>
+</trkpt>
+<trkpt lat="-30.677150" lon="150.607183">
+<time>2004-04-24T04:01:50Z</time>
+</trkpt>
+<trkpt lat="-30.677133" lon="150.609417">
+<time>2004-04-24T04:02:20Z</time>
+</trkpt>
+<trkpt lat="-30.678150" lon="150.607183">
+<time>2004-04-24T04:02:51Z</time>
+</trkpt>
+<trkpt lat="-30.675917" lon="150.604500">
+<time>2004-04-24T04:03:22Z</time>
+</trkpt>
+<trkpt lat="-30.678667" lon="150.605933">
+<time>2004-04-24T04:03:52Z</time>
+</trkpt>
+<trkpt lat="-30.681400" lon="150.607533">
+<time>2004-04-24T04:04:23Z</time>
+</trkpt>
+<trkpt lat="-30.677633" lon="150.606983">
+<time>2004-04-24T04:04:54Z</time>
+</trkpt>
+<trkpt lat="-30.680183" lon="150.608450">
+<time>2004-04-24T04:05:24Z</time>
+</trkpt>
+<trkpt lat="-30.678133" lon="150.606300">
+<time>2004-04-24T04:05:55Z</time>
+</trkpt>
+<trkpt lat="-30.678567" lon="150.606817">
+<time>2004-04-24T04:06:26Z</time>
+</trkpt>
+<trkpt lat="-30.678100" lon="150.606517">
+<time>2004-04-24T04:06:57Z</time>
+</trkpt>
+<trkpt lat="-30.678800" lon="150.606983">
+<time>2004-04-24T04:07:28Z</time>
+</trkpt>
+<trkpt lat="-30.677600" lon="150.606100">
+<time>2004-04-24T04:07:58Z</time>
+</trkpt>
+<trkpt lat="-30.679750" lon="150.607450">
+<time>2004-04-24T04:08:29Z</time>
+</trkpt>
+<trkpt lat="-30.680367" lon="150.607217">
+<time>2004-04-24T04:09:00Z</time>
+</trkpt>
+<trkpt lat="-30.679917" lon="150.608917">
+<time>2004-04-24T04:09:31Z</time>
+</trkpt>
+<trkpt lat="-30.680150" lon="150.609550">
+<time>2004-04-24T04:10:01Z</time>
+</trkpt>
+<trkpt lat="-30.680317" lon="150.607400">
+<time>2004-04-24T04:10:32Z</time>
+</trkpt>
+<trkpt lat="-30.679750" lon="150.605067">
+<time>2004-04-24T04:11:02Z</time>
+</trkpt>
+<trkpt lat="-30.680267" lon="150.605450">
+<time>2004-04-24T04:11:32Z</time>
+</trkpt>
+<trkpt lat="-30.680767" lon="150.606617">
+<time>2004-04-24T04:12:03Z</time>
+</trkpt>
+<trkpt lat="-30.680217" lon="150.608383">
+<time>2004-04-24T04:12:34Z</time>
+</trkpt>
+<trkpt lat="-30.679333" lon="150.608467">
+<time>2004-04-24T04:13:04Z</time>
+</trkpt>
+<trkpt lat="-30.678233" lon="150.605317">
+<time>2004-04-24T04:13:35Z</time>
+</trkpt>
+<trkpt lat="-30.675633" lon="150.602533">
+<time>2004-04-24T04:14:06Z</time>
+</trkpt>
+<trkpt lat="-30.678250" lon="150.605133">
+<time>2004-04-24T04:14:37Z</time>
+</trkpt>
+<trkpt lat="-30.679300" lon="150.607017">
+<time>2004-04-24T04:15:07Z</time>
+</trkpt>
+<trkpt lat="-30.679433" lon="150.608167">
+<time>2004-04-24T04:15:38Z</time>
+</trkpt>
+<trkpt lat="-30.679217" lon="150.608217">
+<time>2004-04-24T04:16:08Z</time>
+</trkpt>
+<trkpt lat="-30.678983" lon="150.608233">
+<time>2004-04-24T04:16:39Z</time>
+</trkpt>
+<trkpt lat="-30.679167" lon="150.608783">
+<time>2004-04-24T04:17:10Z</time>
+</trkpt>
+<trkpt lat="-30.680317" lon="150.606550">
+<time>2004-04-24T04:17:41Z</time>
+</trkpt>
+<trkpt lat="-30.680100" lon="150.605867">
+<time>2004-04-24T04:18:11Z</time>
+</trkpt>
+<trkpt lat="-30.676150" lon="150.605733">
+<time>2004-04-24T04:18:41Z</time>
+</trkpt>
+<trkpt lat="-30.675433" lon="150.604217">
+<time>2004-04-24T04:19:12Z</time>
+</trkpt>
+<trkpt lat="-30.677433" lon="150.605567">
+<time>2004-04-24T04:19:43Z</time>
+</trkpt>
+<trkpt lat="-30.679900" lon="150.606117">
+<time>2004-04-24T04:20:13Z</time>
+</trkpt>
+<trkpt lat="-30.678450" lon="150.606117">
+<time>2004-04-24T04:20:44Z</time>
+</trkpt>
+<trkpt lat="-30.677967" lon="150.607050">
+<time>2004-04-24T04:21:14Z</time>
+</trkpt>
+<trkpt lat="-30.677633" lon="150.606900">
+<time>2004-04-24T04:21:44Z</time>
+</trkpt>
+<trkpt lat="-30.679533" lon="150.605967">
+<time>2004-04-24T04:22:15Z</time>
+</trkpt>
+<trkpt lat="-30.678700" lon="150.607050">
+<time>2004-04-24T04:22:45Z</time>
+</trkpt>
+<trkpt lat="-30.678617" lon="150.607617">
+<time>2004-04-24T04:23:15Z</time>
+</trkpt>
+<trkpt lat="-30.678333" lon="150.607400">
+<time>2004-04-24T04:23:45Z</time>
+</trkpt>
+<trkpt lat="-30.678150" lon="150.607617">
+<time>2004-04-24T04:24:16Z</time>
+</trkpt>
+<trkpt lat="-30.679217" lon="150.608100">
+<time>2004-04-24T04:24:47Z</time>
+</trkpt>
+<trkpt lat="-30.679167" lon="150.605483">
+<time>2004-04-24T04:25:18Z</time>
+</trkpt>
+<trkpt lat="-30.679900" lon="150.604000">
+<time>2004-04-24T04:25:48Z</time>
+</trkpt>
+<trkpt lat="-30.678133" lon="150.605883">
+<time>2004-04-24T04:26:19Z</time>
+</trkpt>
+<trkpt lat="-30.677983" lon="150.606667">
+<time>2004-04-24T04:26:49Z</time>
+</trkpt>
+<trkpt lat="-30.679233" lon="150.605533">
+<time>2004-04-24T04:27:20Z</time>
+</trkpt>
+<trkpt lat="-30.678217" lon="150.605267">
+<time>2004-04-24T04:27:51Z</time>
+</trkpt>
+<trkpt lat="-30.681967" lon="150.604800">
+<time>2004-04-24T04:28:21Z</time>
+</trkpt>
+<trkpt lat="-30.686217" lon="150.605267">
+<time>2004-04-24T04:28:51Z</time>
+</trkpt>
+<trkpt lat="-30.691117" lon="150.606767">
+<time>2004-04-24T04:29:22Z</time>
+</trkpt>
+<trkpt lat="-30.695867" lon="150.609117">
+<time>2004-04-24T04:29:52Z</time>
+</trkpt>
+<trkpt lat="-30.700367" lon="150.611350">
+<time>2004-04-24T04:30:23Z</time>
+</trkpt>
+<trkpt lat="-30.704800" lon="150.615100">
+<time>2004-04-24T04:30:54Z</time>
+</trkpt>
+<trkpt lat="-30.705983" lon="150.617467">
+<time>2004-04-24T04:31:24Z</time>
+</trkpt>
+<trkpt lat="-30.707167" lon="150.619183">
+<time>2004-04-24T04:31:55Z</time>
+</trkpt>
+<trkpt lat="-30.705800" lon="150.620467">
+<time>2004-04-24T04:32:26Z</time>
+</trkpt>
+<trkpt lat="-30.706833" lon="150.620933">
+<time>2004-04-24T04:32:57Z</time>
+</trkpt>
+<trkpt lat="-30.710200" lon="150.620783">
+<time>2004-04-24T04:33:27Z</time>
+</trkpt>
+<trkpt lat="-30.714283" lon="150.622433">
+<time>2004-04-24T04:33:58Z</time>
+</trkpt>
+<trkpt lat="-30.717733" lon="150.627250">
+<time>2004-04-24T04:34:29Z</time>
+</trkpt>
+<trkpt lat="-30.719467" lon="150.630800">
+<time>2004-04-24T04:35:00Z</time>
+</trkpt>
+<trkpt lat="-30.719883" lon="150.633083">
+<time>2004-04-24T04:35:30Z</time>
+</trkpt>
+<trkpt lat="-30.719700" lon="150.630417">
+<time>2004-04-24T04:36:01Z</time>
+</trkpt>
+<trkpt lat="-30.719733" lon="150.631917">
+<time>2004-04-24T04:36:31Z</time>
+</trkpt>
+<trkpt lat="-30.718700" lon="150.633767">
+<time>2004-04-24T04:37:02Z</time>
+</trkpt>
+<trkpt lat="-30.718933" lon="150.633167">
+<time>2004-04-24T04:37:33Z</time>
+</trkpt>
+<trkpt lat="-30.718850" lon="150.634383">
+<time>2004-04-24T04:38:03Z</time>
+</trkpt>
+<trkpt lat="-30.720150" lon="150.633233">
+<time>2004-04-24T04:38:33Z</time>
+</trkpt>
+<trkpt lat="-30.724150" lon="150.635250">
+<time>2004-04-24T04:39:04Z</time>
+</trkpt>
+<trkpt lat="-30.727817" lon="150.639733">
+<time>2004-04-24T04:39:35Z</time>
+</trkpt>
+<trkpt lat="-30.729767" lon="150.645800">
+<time>2004-04-24T04:40:05Z</time>
+</trkpt>
+<trkpt lat="-30.729050" lon="150.647483">
+<time>2004-04-24T04:40:35Z</time>
+</trkpt>
+<trkpt lat="-30.729550" lon="150.647400">
+<time>2004-04-24T04:41:05Z</time>
+</trkpt>
+<trkpt lat="-30.730067" lon="150.648850">
+<time>2004-04-24T04:41:35Z</time>
+</trkpt>
+<trkpt lat="-30.729633" lon="150.650850">
+<time>2004-04-24T04:42:05Z</time>
+</trkpt>
+<trkpt lat="-30.730250" lon="150.647650">
+<time>2004-04-24T04:42:36Z</time>
+</trkpt>
+<trkpt lat="-30.730733" lon="150.648850">
+<time>2004-04-24T04:43:07Z</time>
+</trkpt>
+<trkpt lat="-30.731433" lon="150.648783">
+<time>2004-04-24T04:43:38Z</time>
+</trkpt>
+<trkpt lat="-30.730750" lon="150.649117">
+<time>2004-04-24T04:44:09Z</time>
+</trkpt>
+<trkpt lat="-30.731733" lon="150.647700">
+<time>2004-04-24T04:44:40Z</time>
+</trkpt>
+<trkpt lat="-30.732233" lon="150.645933">
+<time>2004-04-24T04:45:11Z</time>
+</trkpt>
+<trkpt lat="-30.734867" lon="150.646233">
+<time>2004-04-24T04:45:42Z</time>
+</trkpt>
+<trkpt lat="-30.735833" lon="150.652400">
+<time>2004-04-24T04:46:13Z</time>
+</trkpt>
+<trkpt lat="-30.739500" lon="150.654133">
+<time>2004-04-24T04:46:44Z</time>
+</trkpt>
+<trkpt lat="-30.743233" lon="150.656917">
+<time>2004-04-24T04:47:15Z</time>
+</trkpt>
+<trkpt lat="-30.746733" lon="150.661900">
+<time>2004-04-24T04:47:46Z</time>
+</trkpt>
+<trkpt lat="-30.749383" lon="150.667067">
+<time>2004-04-24T04:48:16Z</time>
+</trkpt>
+<trkpt lat="-30.749700" lon="150.673850">
+<time>2004-04-24T04:48:47Z</time>
+</trkpt>
+<trkpt lat="-30.749850" lon="150.676217">
+<time>2004-04-24T04:49:18Z</time>
+</trkpt>
+<trkpt lat="-30.752567" lon="150.680133">
+<time>2004-04-24T04:49:48Z</time>
+</trkpt>
+<trkpt lat="-30.755700" lon="150.684917">
+<time>2004-04-24T04:50:19Z</time>
+</trkpt>
+<trkpt lat="-30.758067" lon="150.689717">
+<time>2004-04-24T04:50:49Z</time>
+</trkpt>
+<trkpt lat="-30.759917" lon="150.696183">
+<time>2004-04-24T04:51:19Z</time>
+</trkpt>
+<trkpt lat="-30.762400" lon="150.703417">
+<time>2004-04-24T04:51:50Z</time>
+</trkpt>
+<trkpt lat="-30.764067" lon="150.710700">
+<time>2004-04-24T04:52:21Z</time>
+</trkpt>
+<trkpt lat="-30.765600" lon="150.715867">
+<time>2004-04-24T04:52:51Z</time>
+</trkpt>
+<trkpt lat="-30.763983" lon="150.721917">
+<time>2004-04-24T04:53:21Z</time>
+</trkpt>
+<trkpt lat="-30.765250" lon="150.725983">
+<time>2004-04-24T04:53:52Z</time>
+</trkpt>
+<trkpt lat="-30.765300" lon="150.723817">
+<time>2004-04-24T04:54:23Z</time>
+</trkpt>
+<trkpt lat="-30.765550" lon="150.725650">
+<time>2004-04-24T04:54:53Z</time>
+</trkpt>
+<trkpt lat="-30.763617" lon="150.724283">
+<time>2004-04-24T04:55:24Z</time>
+</trkpt>
+<trkpt lat="-30.763717" lon="150.723967">
+<time>2004-04-24T04:55:54Z</time>
+</trkpt>
+<trkpt lat="-30.763850" lon="150.723700">
+<time>2004-04-24T04:56:25Z</time>
+</trkpt>
+<trkpt lat="-30.764083" lon="150.723567">
+<time>2004-04-24T04:56:56Z</time>
+</trkpt>
+<trkpt lat="-30.764133" lon="150.723400">
+<time>2004-04-24T04:57:19Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<rte>
+  <name>0001</name>
+  <desc>IGCDATE000000: </desc>
+  <rtept lat="-30.675867" lon="150.609033">
+    <name>TAKEOFF</name>
+    <cmt>BORAH </cmt>
+  </rtept>
+  <rtept lat="-30.675867" lon="150.609033">
+    <name>START</name>
+    <cmt>BORAH </cmt>
+  </rtept>
+  <rtept lat="-30.729933" lon="150.649200">
+    <name>TURN01</name>
+    <cmt>BALDWI</cmt>
+  </rtept>
+  <rtept lat="-30.763683" lon="150.723200">
+    <name>FINISH</name>
+    <cmt>MANAIR</cmt>
+  </rtept>
+  <rtept lat="-30.763683" lon="150.723200">
+    <name>LANDING</name>
+    <cmt>MANAIR</cmt>
+  </rtept>
+</rte>
+</gpx>
diff --git a/gpsbabel/reference/igc1_igc.out b/gpsbabel/reference/igc1_igc.out
new file mode 100644 (file)
index 0000000..beb86d8
--- /dev/null
@@ -0,0 +1,123 @@
+AXXXZZZGPSBabel\r
+HFDTE240404\r
+HFPLTPILOT:CHRIS JONES\r
+C010170000000000000000101\r
+C3040552S15036542EBORAH \r
+C3040552S15036542EBORAH \r
+C3043796S15038952EBALDWI\r
+C3045821S15043392EMANAIR\r
+C3045821S15043392EMANAIR\r
+B0400193040579S15036384EA0000000000\r
+B0400493040614S15036317EA0000000000\r
+B0401193040699S15036326EA0000000000\r
+B0401503040629S15036431EA0000000000\r
+B0402203040628S15036565EA0000000000\r
+B0402513040689S15036431EA0000000000\r
+B0403223040555S15036270EA0000000000\r
+B0403523040720S15036356EA0000000000\r
+B0404233040884S15036452EA0000000000\r
+B0404543040658S15036419EA0000000000\r
+B0405243040811S15036507EA0000000000\r
+B0405553040688S15036378EA0000000000\r
+B0406263040714S15036409EA0000000000\r
+B0406573040686S15036391EA0000000000\r
+B0407283040728S15036419EA0000000000\r
+B0407583040656S15036366EA0000000000\r
+B0408293040785S15036447EA0000000000\r
+B0409003040822S15036433EA0000000000\r
+B0409313040795S15036535EA0000000000\r
+B0410013040809S15036573EA0000000000\r
+B0410323040819S15036444EA0000000000\r
+B0411023040785S15036304EA0000000000\r
+B0411323040816S15036327EA0000000000\r
+B0412033040846S15036397EA0000000000\r
+B0412343040813S15036503EA0000000000\r
+B0413043040760S15036508EA0000000000\r
+B0413353040694S15036319EA0000000000\r
+B0414063040538S15036152EA0000000000\r
+B0414373040695S15036308EA0000000000\r
+B0415073040758S15036421EA0000000000\r
+B0415383040766S15036490EA0000000000\r
+B0416083040753S15036493EA0000000000\r
+B0416393040739S15036494EA0000000000\r
+B0417103040750S15036527EA0000000000\r
+B0417413040819S15036393EA0000000000\r
+B0418113040806S15036352EA0000000000\r
+B0418413040569S15036344EA0000000000\r
+B0419123040526S15036253EA0000000000\r
+B0419433040646S15036334EA0000000000\r
+B0420133040794S15036367EA0000000000\r
+B0420443040707S15036367EA0000000000\r
+B0421143040678S15036423EA0000000000\r
+B0421443040658S15036414EA0000000000\r
+B0422153040772S15036358EA0000000000\r
+B0422453040722S15036423EA0000000000\r
+B0423153040717S15036457EA0000000000\r
+B0423453040700S15036444EA0000000000\r
+B0424163040689S15036457EA0000000000\r
+B0424473040753S15036486EA0000000000\r
+B0425183040750S15036329EA0000000000\r
+B0425483040794S15036240EA0000000000\r
+B0426193040688S15036353EA0000000000\r
+B0426493040679S15036400EA0000000000\r
+B0427203040754S15036332EA0000000000\r
+B0427513040693S15036316EA0000000000\r
+B0428213040918S15036288EA0000000000\r
+B0428513041173S15036316EA0000000000\r
+B0429223041467S15036406EA0000000000\r
+B0429523041752S15036547EA0000000000\r
+B0430233042022S15036681EA0000000000\r
+B0430543042288S15036906EA0000000000\r
+B0431243042359S15037048EA0000000000\r
+B0431553042430S15037151EA0000000000\r
+B0432263042348S15037228EA0000000000\r
+B0432573042410S15037256EA0000000000\r
+B0433273042612S15037247EA0000000000\r
+B0433583042857S15037346EA0000000000\r
+B0434293043064S15037635EA0000000000\r
+B0435003043168S15037848EA0000000000\r
+B0435303043193S15037985EA0000000000\r
+B0436013043182S15037825EA0000000000\r
+B0436313043184S15037915EA0000000000\r
+B0437023043122S15038026EA0000000000\r
+B0437333043136S15037990EA0000000000\r
+B0438033043131S15038063EA0000000000\r
+B0438333043209S15037994EA0000000000\r
+B0439043043449S15038115EA0000000000\r
+B0439353043669S15038384EA0000000000\r
+B0440053043786S15038748EA0000000000\r
+B0440353043743S15038849EA0000000000\r
+B0441053043773S15038844EA0000000000\r
+B0441353043804S15038931EA0000000000\r
+B0442053043778S15039051EA0000000000\r
+B0442363043815S15038859EA0000000000\r
+B0443073043844S15038931EA0000000000\r
+B0443383043886S15038927EA0000000000\r
+B0444093043845S15038947EA0000000000\r
+B0444403043904S15038862EA0000000000\r
+B0445113043934S15038756EA0000000000\r
+B0445423044092S15038774EA0000000000\r
+B0446133044150S15039144EA0000000000\r
+B0446443044370S15039248EA0000000000\r
+B0447153044594S15039415EA0000000000\r
+B0447463044804S15039714EA0000000000\r
+B0448163044963S15040024EA0000000000\r
+B0448473044982S15040431EA0000000000\r
+B0449183044991S15040573EA0000000000\r
+B0449483045154S15040808EA0000000000\r
+B0450193045342S15041095EA0000000000\r
+B0450493045484S15041383EA0000000000\r
+B0451193045595S15041771EA0000000000\r
+B0451503045744S15042205EA0000000000\r
+B0452213045844S15042642EA0000000000\r
+B0452513045936S15042952EA0000000000\r
+B0453213045839S15043315EA0000000000\r
+B0453523045915S15043559EA0000000000\r
+B0454233045918S15043429EA0000000000\r
+B0454533045933S15043539EA0000000000\r
+B0455243045817S15043457EA0000000000\r
+B0455543045823S15043438EA0000000000\r
+B0456253045831S15043422EA0000000000\r
+B0456563045845S15043414EA0000000000\r
+B0457193045848S15043404EA0000000000\r
+GGPSBabelSecurityRecordGuaranteedToFailVALIChecks\r
diff --git a/gpsbabel/reference/igc2.igc b/gpsbabel/reference/igc2.igc
new file mode 100644 (file)
index 0000000..b075941
--- /dev/null
@@ -0,0 +1,46 @@
+AXXXABCFLIGHT:1\r
+HFFXA035\r
+HFDTE160701\r
+HFPLTPILOT:Bill Bloggs\r
+HFGTYGLIDERTYPE:Schleicher ASH-25\r
+HFGIDGLIDERID:ABCD-1234\r
+HFDTM100GPSDATUM:WGS-1984\r
+HFRFWFIRMWAREVERSION:6.4\r
+HFRHWHARDWAREVERSION:3.0\r
+HFFTYFRTYPE:Manufacturer Model\r
+HFGPSMarconiCanada:Superstar 12ch max10000m\r
+HFPRSPRESSALTSENSOR:Sensyn XYZ1111 max11000m\r
+HFCIDCOMPETITIONID:XYZ-78910\r
+HFCCLCOMPETITIONCLASS:15mMotor Glider\r
+HFSCMSECONDCREW:JohnSmith\r
+I023638FXA3941ENL\r
+J010812HDT\r
+C150701213841160701000102500KTri\r
+C5111359N00101899WLashamClubhouse\r
+C5110179N00102644WLashamStart S Start\r
+C5209092N00255227WSarnesfield TP1\r
+C5230147N00017612WNormanCross TP2\r
+C5110179N00102644WLashamStart S Finish\r
+C5111359N00101899WLashamClubhouse\r
+F160240040609123624221821\r
+B1602405407121N00249342WA0028000421020509950\r
+D20331\r
+E160245PEV\r
+B1602455107126N00149300WA0028800429019509020\r
+B1602505107134N00149283WA0029000432021009015\r
+B1602555107140N00149221WA0029000430020009012\r
+F1603000609123624221821\r
+B1603005107150N00149202WA0029100432025608009\r
+E160305PEV\r
+B1603055107180N00149185WA0029100435021008015\r
+B1603105107212N00149174WA0029300435019608024\r
+K160248090\r
+B1602485107220N00149150WA0049400436019008018\r
+B1602525107330N00149127WA0049600439019508015\r
+LXXXRURITANIANSTANDARD NATIONALS DAY 1\r
+LXXXFLIGHTTIME: 4:14:25 TASK SPEED:58.48KTS\r
+GREJNGJERJKNJKRE31895478537H43982FJN9248F942389T433T\r
+GJNJK1489IERGNV3089IVJE9GO398535J3894N358954983O0934\r
+GSKTO5427FGTNUT5621WKTC6714FT8957FGMKJ134527FGTR6751\r
+GK2489IERGNV3089IVJE39GO398535J3894N358954983FTGY546\r
+G12560DJUWT28719GTAOL5628FGWNIST78154INWTOLP7815FITN\r
diff --git a/gpsbabel/reference/igc2_gpx.out b/gpsbabel/reference/igc2_gpx.out
new file mode 100644 (file)
index 0000000..8655b69
--- /dev/null
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<gpx
+ version="1.0"
+creator="GPSBabel - http://gpsbabel.sourceforge.net"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns="http://www.topografix.com/GPX/1/0"
+xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
+<time>1970-01-01T00:00:00Z</time>
+<trk>
+  <name>PRESALTTRK</name>
+  <desc>IGCHDRS~HFFXA035~HFPLTPILOT:Bill Bloggs~HFGTYGLIDERTYPE:Schleicher ASH-25~HFGIDGLIDERID:ABCD-1234~HFDTM100GPSDATUM:WGS-1984~HFRFWFIRMWAREVERSION:6.4~HFRHWHARDWAREVERSION:3.0~HFFTYFRTYPE:Manufacturer Model~HFGPSMarconiCanada:Superstar 12ch max10000m~HFPRSPRESSALTSENSOR:Sensyn XYZ1111 max11000m~HFCIDCOMPETITIONID:XYZ-78910~HFCCLCOMPETITIONCLASS:15mMotor Glider~HFSCMSECONDCREW:JohnSmith~</desc>
+<trkseg>
+<trkpt lat="54.118683" lon="-2.822367">
+<ele>280.000000</ele>
+<time>2001-07-16T16:02:40Z</time>
+</trkpt>
+<trkpt lat="51.118767" lon="-1.821667">
+<ele>288.000000</ele>
+<time>2001-07-16T16:02:45Z</time>
+</trkpt>
+<trkpt lat="51.118900" lon="-1.821383">
+<ele>290.000000</ele>
+<time>2001-07-16T16:02:50Z</time>
+</trkpt>
+<trkpt lat="51.119000" lon="-1.820350">
+<ele>290.000000</ele>
+<time>2001-07-16T16:02:55Z</time>
+</trkpt>
+<trkpt lat="51.119167" lon="-1.820033">
+<ele>291.000000</ele>
+<time>2001-07-16T16:03:00Z</time>
+</trkpt>
+<trkpt lat="51.119667" lon="-1.819750">
+<ele>291.000000</ele>
+<time>2001-07-16T16:03:05Z</time>
+</trkpt>
+<trkpt lat="51.120200" lon="-1.819567">
+<ele>293.000000</ele>
+<time>2001-07-16T16:03:10Z</time>
+</trkpt>
+<trkpt lat="51.120333" lon="-1.819167">
+<ele>494.000000</ele>
+<time>2001-07-17T16:02:48Z</time>
+</trkpt>
+<trkpt lat="51.122167" lon="-1.818783">
+<ele>496.000000</ele>
+<time>2001-07-17T16:02:52Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<trk>
+  <name>GNSSALTTRK</name>
+  <desc>IGCHDRS~HFFXA035~HFPLTPILOT:Bill Bloggs~HFGTYGLIDERTYPE:Schleicher ASH-25~HFGIDGLIDERID:ABCD-1234~HFDTM100GPSDATUM:WGS-1984~HFRFWFIRMWAREVERSION:6.4~HFRHWHARDWAREVERSION:3.0~HFFTYFRTYPE:Manufacturer Model~HFGPSMarconiCanada:Superstar 12ch max10000m~HFPRSPRESSALTSENSOR:Sensyn XYZ1111 max11000m~HFCIDCOMPETITIONID:XYZ-78910~HFCCLCOMPETITIONCLASS:15mMotor Glider~HFSCMSECONDCREW:JohnSmith~</desc>
+<trkseg>
+<trkpt lat="54.118683" lon="-2.822367">
+<ele>421.000000</ele>
+<time>2001-07-16T16:02:40Z</time>
+</trkpt>
+<trkpt lat="51.118767" lon="-1.821667">
+<ele>429.000000</ele>
+<time>2001-07-16T16:02:45Z</time>
+</trkpt>
+<trkpt lat="51.118900" lon="-1.821383">
+<ele>432.000000</ele>
+<time>2001-07-16T16:02:50Z</time>
+</trkpt>
+<trkpt lat="51.119000" lon="-1.820350">
+<ele>430.000000</ele>
+<time>2001-07-16T16:02:55Z</time>
+</trkpt>
+<trkpt lat="51.119167" lon="-1.820033">
+<ele>432.000000</ele>
+<time>2001-07-16T16:03:00Z</time>
+</trkpt>
+<trkpt lat="51.119667" lon="-1.819750">
+<ele>435.000000</ele>
+<time>2001-07-16T16:03:05Z</time>
+</trkpt>
+<trkpt lat="51.120200" lon="-1.819567">
+<ele>435.000000</ele>
+<time>2001-07-16T16:03:10Z</time>
+</trkpt>
+<trkpt lat="51.120333" lon="-1.819167">
+<ele>436.000000</ele>
+<time>2001-07-17T16:02:48Z</time>
+</trkpt>
+<trkpt lat="51.122167" lon="-1.818783">
+<ele>439.000000</ele>
+<time>2001-07-17T16:02:52Z</time>
+</trkpt>
+</trkseg>
+</trk>
+<rte>
+  <name>0001</name>
+  <desc>IGCDATE160701: 500KTri</desc>
+  <rtept lat="51.189317" lon="-1.031650">
+<time>2001-07-15T21:38:41Z</time>
+    <name>TAKEOFF</name>
+    <cmt>LashamClubhouse</cmt>
+  </rtept>
+  <rtept lat="51.169650" lon="-1.044067">
+<time>2001-07-15T21:38:41Z</time>
+    <name>START</name>
+    <cmt>LashamStart S Start</cmt>
+  </rtept>
+  <rtept lat="52.151533" lon="-2.920450">
+<time>2001-07-15T21:38:41Z</time>
+    <name>TURN01</name>
+    <cmt>Sarnesfield TP1</cmt>
+  </rtept>
+  <rtept lat="52.502450" lon="-0.293533">
+<time>2001-07-15T21:38:41Z</time>
+    <name>TURN02</name>
+    <cmt>NormanCross TP2</cmt>
+  </rtept>
+  <rtept lat="51.169650" lon="-1.044067">
+<time>2001-07-15T21:38:41Z</time>
+    <name>FINISH</name>
+    <cmt>LashamStart S Finish</cmt>
+  </rtept>
+  <rtept lat="51.189317" lon="-1.031650">
+<time>2001-07-15T21:38:41Z</time>
+    <name>LANDING</name>
+    <cmt>LashamClubhouse</cmt>
+  </rtept>
+</rte>
+</gpx>
diff --git a/gpsbabel/reference/igc2_igc.out b/gpsbabel/reference/igc2_igc.out
new file mode 100644 (file)
index 0000000..877a5bc
--- /dev/null
@@ -0,0 +1,32 @@
+AXXXZZZGPSBabel\r
+HFDTE160701\r
+HFFXA035\r
+HFPLTPILOT:Bill Bloggs\r
+HFGTYGLIDERTYPE:Schleicher ASH-25\r
+HFGIDGLIDERID:ABCD-1234\r
+HFDTM100GPSDATUM:WGS-1984\r
+HFRFWFIRMWAREVERSION:6.4\r
+HFRHWHARDWAREVERSION:3.0\r
+HFFTYFRTYPE:Manufacturer Model\r
+HFGPSMarconiCanada:Superstar 12ch max10000m\r
+HFPRSPRESSALTSENSOR:Sensyn XYZ1111 max11000m\r
+HFCIDCOMPETITIONID:XYZ-78910\r
+HFCCLCOMPETITIONCLASS:15mMotor Glider\r
+HFSCMSECONDCREW:JohnSmith\r
+C150701213841160701000102500KTri\r
+C5111359N00101899WLashamClubhouse\r
+C5110179N00102644WLashamStart S Start\r
+C5209092N00255227WSarnesfield TP1\r
+C5230147N00017612WNormanCross TP2\r
+C5110179N00102644WLashamStart S Finish\r
+C5111359N00101899WLashamClubhouse\r
+B1602405407121N00249342WA0028000421\r
+B1602455107126N00149300WA0028800429\r
+B1602505107134N00149283WA0029000432\r
+B1602555107140N00149221WA0029000430\r
+B1603005107150N00149202WA0029100432\r
+B1603055107180N00149185WA0029100435\r
+B1603105107212N00149174WA0029300435\r
+B1602485107220N00149150WA0049400436\r
+B1602525107330N00149127WA0049600439\r
+GGPSBabelSecurityRecordGuaranteedToFailVALIChecks\r
index 70c6dd4eadf02492b9af6bb4da1bb9d5bb4ce9fc..0f07330877182e90ba8e7722e1a1df9e1668917f 100644 (file)
@@ -70,6 +70,14 @@ route_add_head(route_head *rte)
        rte_head_ct++;
 }
 
+void
+route_del_head(route_head *rte)
+{
+       dequeue( &rte->Q );
+       route_free( rte );
+       rte_head_ct--;
+}
+
 void
 track_add_head(route_head *rte)
 {
@@ -78,6 +86,14 @@ track_add_head(route_head *rte)
        trk_head_ct++;
 }
 
+void
+track_del_head(route_head *rte)
+{
+       dequeue( &rte->Q );
+       route_free( rte );
+       trk_head_ct--;
+}
+
 void
 route_add_wpt(route_head *rte, waypoint *wpt)
 {
index 07a0eff1c03930b2b43a319fb5d3d893e3290d92..677bb9bf0cde89f44d7a5c904f78cd5f7133004f 100755 (executable)
@@ -522,6 +522,33 @@ rm -f ${TMPDIR}/netstumbler.mps
 ${PNAME} -i netstumbler -f reference/netstumbler.txt -o mapsource -F ${TMPDIR}/netstumbler.mps
 bincompare ${TMPDIR}/netstumbler.mps reference/netstumbler.mps
 
+#
+# IGC tests
+#
+rm -f ${TMPDIR}/igc*out
+${PNAME} -i gpx -f reference/igc1.gpx -o igc -F ${TMPDIR}/igc.out
+sed '/^LXXXGenerated by GPSBabel Version/d' ${TMPDIR}/igc.out > ${TMPDIR}/igc_sed.out
+compare ${TMPDIR}/igc_sed.out reference/igc1_igc.out
+
+${PNAME} -i igc -f ${TMPDIR}/igc.out -o gpx -F ${TMPDIR}/igc.gpx
+compare ${TMPDIR}/igc.gpx reference/igc1_gpx.out
+
+${PNAME} -i gpx -f ${TMPDIR}/igc.gpx -o igc -F ${TMPDIR}/igc.out
+sed '/^LXXXGenerated by GPSBabel Version/d' ${TMPDIR}/igc.out > ${TMPDIR}/igc_sed.out
+compare ${TMPDIR}/igc_sed.out reference/igc1_igc.out
+
+
+${PNAME} -i igc -f reference/igc2.igc -o gpx -F ${TMPDIR}/igc.gpx
+compare ${TMPDIR}/igc.gpx reference/igc2_gpx.out
+
+${PNAME} -i gpx -f ${TMPDIR}/igc.gpx -o igc -F ${TMPDIR}/igc.out
+sed '/^LXXXGenerated by GPSBabel Version/d' ${TMPDIR}/igc.out > ${TMPDIR}/igc_sed.out
+compare ${TMPDIR}/igc_sed.out reference/igc2_igc.out
+
+${PNAME} -i igc -f ${TMPDIR}/igc.out -o gpx -F ${TMPDIR}/igc.gpx
+compare ${TMPDIR}/igc.gpx reference/igc2_gpx.out
+
+
 #
 # XCSV "human readable" tests
 #
index dba2832e3e1bdd96ecf880cdcecadd58d546d371..32d54baebcf97232343937c1465ae66a37183a65 100644 (file)
@@ -255,6 +255,26 @@ xfopen(const char *fname, const char *type, const char *errtxt)
        return f;
 }
 
+void
+xfprintf(const char *errtxt, FILE *stream, const char *format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       if (vfprintf(stream, format, ap) < 0) {
+               fatal("%s writing output file.  Error was '%s'.\n",
+                               errtxt, strerror(errno));
+       }
+       va_end(ap);
+}
+
+void
+xfputs(const char *errtxt, const char *s, FILE *stream)
+{
+       if (fputs(s, stream) < 0) {
+               fatal("%s Writing output file.  Error was '%s'.\n",
+                               errtxt, strerror(errno));
+       }
+}
 
 /* 
  * Duplicate a pascal string into a normal C string.
index 4cbfa8973750dcedb93e6bb5b5930eceae99a3ca..c82a96c3d04ef343296d4b46c5501d79cf0b4102 100644 (file)
@@ -65,6 +65,7 @@ extern ff_vecs_t palmdoc_vecs;
 extern ff_vecs_t html_vecs;
 extern ff_vecs_t netstumbler_vecs;
 extern ff_vecs_t HsaEndeavourNavigator_vecs;
+extern ff_vecs_t igc_vecs;
 
 static
 vecs_t vec_list[] = {
@@ -279,6 +280,12 @@ vecs_t vec_list[] = {
                "HSA Endeavour Navigator export File",
                NULL
        },
+        {
+                &igc_vecs,
+                "igc",
+                "FAI/IGC Flight Recorder Data Format",
+                NULL
+        },
        {
                NULL,
                NULL,